//    Copyright (C) Kherty.  All rights reserved.
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Text;
using OpenLS.Spreadsheet.Extensibility;

namespace OpenLS.Spreadsheet.AnalysisToolPak
{
    internal class Base
    {
        private static Base base16;
        private static Base base2;
        private static Base base8;
        private long[] digits;
        private long maxValue;
        private long minValue;
        private char[] symbols;
        private Dictionary<char, uint> table = new Dictionary<char, uint>();

        private Base(long[] digits, char[] symbols)
        {
            this.digits = digits;
            this.symbols = symbols;
            minValue = -digits[digits.Length - 1];
            maxValue = -minValue - 1;
            for (uint i = 0; i < symbols.Length; i++)
            {
                char c = symbols[i];
                table.Add(c, i);
                char c2 = c.ToString().ToLower()[0];
                if (c2 != c)
                    table.Add(c2, i);
            }
        }

        public static Base Base2
        {
            get
            {
                if (base2 == null)
                {
                    base2 = new Base(GetDigits(2, 11), new char[] {'0', '1'});
                }
                return base2;
            }
        }

        public static Base Base8
        {
            get
            {
                if (base8 == null)
                {
                    base8 = new Base(GetDigits(8,16), new char[] {'0', '1', '2', '3', '4', '5', '6', '7'});
                }
                return base8;
            }
        }

        public static Base Base16
        {
            get
            {
                if (base16 == null)
                    base16 =
                        new Base(GetDigits(16, 12),
                                 new char[]
                                     {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'});
                return base16;
            }
        }

        private static long[] GetDigits(int b, int count)
        {
            long[] result = new long[count];
            long num = 1;
            for (int i = 0; i < count; i++)
            {
                result[i] = num;
                num *= b;
            }
            return result;
        }

        public string GetValue(long n, double places)
        {
            Contract.Requires(n >= 0);
            Contract.Ensures(Contract.Result<string>() != null);
            Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
            long number = (long)n;
            if (number > maxValue)
                throw new ErrorException(Errors.NumError);
            long left = number;
            StringBuilder result = new StringBuilder();
            for (int i = digits.Length - 1; i >= 0; --i)
            {
                long v = left/digits[i];
                if (v > 0 || result.Length > 0)
                    result.Append(symbols[v]);
                Contract.Assert(left >= 0);
                left -= v*digits[i];
                Contract.Assert(left >= 0);
            }
            while (result.Length < places)
            {
                string s = new string('0', (int) places - result.Length);
                result = new StringBuilder(s + result.ToString());
            }
            if (result.Length == 0)
                return "0";
            return result.ToString();
        }

        internal long Parse(string number)
        {
            long result = 0;
            long b = 1;
            int symbolCount = symbols.Length;
            for (int i = number.Length - 1; i >= 0; i--)
            {
                char c = number[i];
                uint v = table[c];
                result += b*v;
                b *= symbolCount;
            }
            return result;
        }
    }
}